/*
 * Copyright 2011 Katsunori Koyanagi
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package sweezy.swf.tools.abcdump
{
	import flash.utils.Dictionary;
	import sweezy.swf.abc.ABC;
	import sweezy.swf.abc.Opcodes;
	import sweezy.swf.abc.structure.ConstantPoolInfo;
	import sweezy.swf.abc.structure.MethodBodyInfo;
	import sweezy.swf.abc.structure.ScriptInfo;

	internal class ABCcontainerDef
	{

		internal var _abc:ABC;

		private var _methodBodyMappings:Dictionary;

		public function ABCcontainerDef(abc:ABC)
		{
			_abc = abc;
		}

		public function abcdump(stream:IDumpStream):void
		{
			var constantPool:ConstantPoolInfo = _abc.constantPool;
			var len:uint;
			var i:uint;

			stream.printSection("int pool");
			for (i = 0, len = constantPool.integerConstants.length; i < len; i++)
			{
				stream.println("// " + i + " : " + constantPool.integerConstants[i]);
			}
			stream.println();

			stream.printSection("uint pool");
			for (i = 0, len = constantPool.unsignedIntegerConstants.length; i < len; i++)
			{
				stream.println("// " + i + " : " + constantPool.unsignedIntegerConstants[i]);
			}
			stream.println();

			stream.printSection("double pool");
			for (i = 0, len = constantPool.doubleConstants.length; i < len; i++)
			{
				stream.println("// " + i + " : " + constantPool.doubleConstants[i]);
			}
			stream.println();

			stream.printSection("string pool");
			for (i = 0, len = constantPool.stringConstants.length; i < len; i++)
			{
				stream.println("// " + i + " : " + constantPool.stringConstants[i]);
			}
			stream.println();

			stream.printSection("namespace pool");
			for (i = 0, len = constantPool.namespaceInfos.length; i < len; i++)
			{
				stream.println("// " + i + " : " + Utils.namespaceToString(constantPool, i));
			}
			stream.println();

			stream.printSection("namespace set pool");
			for (i = 0, len = constantPool.namespaceSetInfos.length; i < len; i++)
			{
				stream.println("// " + i + " : " + Utils.namespaceSetToString(constantPool, i));
			}
			stream.println();

			stream.printSection("multi name pool");
			for (i = 0, len = constantPool.multinameInfos.length; i < len; i++)
			{
				stream.println("// " + i + " : " + Utils.multinameToString(constantPool, i));
			}
			stream.println();

			for each (var scriptInfo:ScriptInfo in _abc.scriptInfos)
			{
				new ScriptDef(this, scriptInfo).dump(stream);
			}
			stream.println();

			stream.printSection("opcodes");
			var codeList:Array = [];
			var totalSize:uint = stream.context.size;
			for (var n:String in stream.context)
			{
				if (n !== "size")
				{
					var size:uint = stream.context[n];
					var code:uint = uint(n);
					codeList[codeList.length] = {size: size, percent: "(" + (size / totalSize * 100).toFixed(2) + "%)", code: code, name: Opcodes.getName(code)};
				}
			}
			codeList.sortOn(["size", "code"], [Array.NUMERIC | Array.DESCENDING, Array.NUMERIC]);
			stream.println("//" + "total(name)       " + totalSize + "(bytes)");
			stream.println();
			for each (var codeInfo:Object in codeList)
			{
				stream.println("//" + (codeInfo.name + "               ").substring(0, 17) + " " + (codeInfo.size + "         ").substring(0, 10) + " " + codeInfo.percent);
			}
		}

		internal function findMethodBody(method:uint):MethodBodyInfo
		{
			if (_methodBodyMappings === null)
			{
				_methodBodyMappings = new Dictionary();

				for each (var methodBodyInfo:MethodBodyInfo in _abc.methodBodyInfos)
				{
					_methodBodyMappings[methodBodyInfo.method] = methodBodyInfo;
				}
			}

			return _methodBodyMappings[method];
		}
	}
}
